package librairies.liste.list;

import librairies.exception.HorsBornes;
import librairies.liste.interfaces.IListe;

/**
 * definition de la liste DC.
 * 
 * @author mlozach
 * @param <E> type de l'element dans la liste.
 */
public class ListeDC<E> implements IListe<E> 
{    
    private Chainon<E> tete;
    private Chainon<E> queue;
    private Chainon<E> courant;
    private int indiceCourant;
    private int taille;
    
    /**
     * constructeur par default.
     */
    public ListeDC() 
    {
    	tete = null;
    	queue = null;
    	courant = null;
    	indiceCourant = -1;
    	taille = 0;
    }

    /**
     * constructeur par recopie.
     * @param init liste des valeurs initials.
     */
    public ListeDC(IListe<E> init) 
    {
    	this();
    	
    	if(init == null)
    	{
    		return;
    	}
    	
    	for (int i = 0; i < init.size(); ++i)
    	{ 
    		this.add(init.get(i));
    	}
    }

    /**
     * permet de se positionner a l'indice demander.
     * @param i l'indice.
     */
	private void positionne(int i) 
	{
		if (i < 0 || i >= taille)
		{
			throw new HorsBornes();
		}
		
		if(i<indiceCourant)
		{
			if(i < indiceCourant - i) 
			{
				for(courant = tete, indiceCourant = 0; indiceCourant < i; indiceCourant++)
				{
					courant = courant.getSuivant();
				}
			}
			else
			{
				for( ; indiceCourant > i; indiceCourant--)
				{
					courant = courant.getPrecedent();
				}
			}
		}
		else 
		{
			if(i - indiceCourant < taille - 1 - i)
			{
				for( ; indiceCourant < i; indiceCourant++)
				{
					courant = courant.getSuivant();
				}
			}
			else
			{
				for(courant = queue, indiceCourant = taille - 1; indiceCourant > i; indiceCourant--)
				{
					courant = courant.getPrecedent();
				}
			}
		}
	}
    
	@Override
	public IListe<E> clear() 
	{
		for(Chainon<E> curseur = tete; curseur!=null; curseur = curseur.getSuivant())
		{
			curseur.setPrecedent(null);
		}
		
		tete = null;
    	queue = null;
    	courant = null;
    	indiceCourant = -1;
    	taille = 0;
    	
		return this;
	}

	@Override
	public E get(int i) throws HorsBornes 
	{
		if (i < 0 || i >= taille)
		{
			throw new HorsBornes();
		}
		
		positionne(i);
		
		return courant.getValeur();
	}

	@Override
	public E set(int i, E e) throws HorsBornes 
	{
		if (i < 0 || i >= taille)
		{
			throw new HorsBornes();
		}
		
		positionne(i);
		E res = courant.getValeur();
		courant.setValeur(e);
		
		return res;
	}

	@Override
	public int contains(Object o) 
	{
		int i; 
		Chainon<E> curseur;
		
		for(curseur = tete, i = 0; curseur != null; curseur = curseur.getSuivant(), i++)
		{
			if(curseur.getValeur().equals(o))
			{
				return i;
			}
		}
		
		return -1;
	}
	
	@Override
	public int containsEquals(Object o, boolean equals) 
	{
		if(equals == true || !(o instanceof String) || !(tete.getValeur() instanceof String)) return contains(o);
		
		int i; 
		Chainon<E> curseur;
		
		for(curseur = tete, i = 0; curseur != null; curseur = curseur.getSuivant(), i++)
		{
			if(curseur.getValeur().toString().contains(o.toString()))
			{
				return i;
			}
		}
		
		return -1;
	}

	@Override
	public boolean containsAll(IListe<?> l) 
	{
		for(int k = 0; k < l.size(); k++) if(contains(l.get(k))==-1)
		{
			return false;
		}
		
		return true;
	}
	   
	@Override
	public IListe<E> add(int i, E e) throws HorsBornes 
	{
		if (i < 0 || i > taille)
		{
			throw new HorsBornes();
		}
		
		Chainon<E> nouveauChainon = new Chainon<E>(e);
		
		if (i == taille) 
		{
			nouveauChainon.setPrecedent(queue);
			queue = nouveauChainon;
			indiceCourant = taille;
		}
		else 
		{
			positionne(i);
			nouveauChainon.setSuivant(courant);
			nouveauChainon.setPrecedent(courant.getPrecedent());
			nouveauChainon.getSuivant().setPrecedent(nouveauChainon);
		}
		
		if (i == 0)
		{
			tete = nouveauChainon;
		}
		else
		{
			nouveauChainon.getPrecedent().setSuivant(nouveauChainon);
		}
		
		courant = nouveauChainon;
		taille++;
		
		return this;
	}
	
	@Override
	public IListe<E> addAll(int i, IListe<? extends E> l) throws HorsBornes 
	{
		if(l == null)
		{
			return this;
		}
		
		for(int k = 0; k < l.size(); k++, i++)
		{
			add(i, l.get(k));
		}
		
		return this;
	}

	@Override
	public E remove(int i) throws HorsBornes 
	{
		if (i < 0 || i >= taille)
		{
			throw new HorsBornes();
		}
		
		positionne(i);
		E res = courant.getValeur();
		
		if(courant.getPrecedent() != null)
		{
			courant.getPrecedent().setSuivant(courant.getSuivant());
		}
		else 
		{
			tete = tete.getSuivant();
		}
		
		if(courant.getSuivant() != null) 
		{
			courant.getSuivant().setPrecedent(courant.getPrecedent());
			courant = courant.getSuivant();
		}
		else 
		{ 
			courant = courant.getPrecedent();
			indiceCourant--;
			queue = courant;
		}
		
		taille--;
		
		return res;
	}
	
	@Override
	public IListe<E> subList(int deb, int fin) throws HorsBornes 
	{
		if (deb < 0 || deb >= taille || fin < 0 || fin >= taille)
		{
			throw new HorsBornes();
		}
		
		ListeDC<E> res = new ListeDC<E>();
		int i; Chainon<E> curseur; 
		
		for(curseur = tete, i = 0; i < deb; curseur = curseur.getSuivant(), i++) {}
		
		for(; i <= fin; curseur = curseur.getSuivant(), i++)
		{ 
			res.add(curseur.getValeur());
		}
		
		return res;
	}
		
	@Override
	public boolean isEmpty() { return taille==0; }
	@Override
	public int size() { return taille; }
	@Override
	public IListe<E> add(E e) {return add(taille, e);}
	@Override
	public IListe<E> addAll(IListe<? extends E> l) { return addAll(taille, l); }
		
	/**
	 * methode Object
	 */
	@Override
	public boolean equals(Object o) 
	{
		if(!(o instanceof ListeDC<?>))
		{
			return false;
		}
		
		ListeDC<?> l = (ListeDC<?>)o;
		
		if(l.taille != taille)
		{
			return false;
		}

		int i; 
		Chainon<E> curseur;
		
		for(curseur = tete, i = 0; curseur != null; curseur = curseur.getSuivant(), i++)
		{
			if(!curseur.getValeur().equals(l.get(i)))
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * methode Object
	 */
	public String toString() 
	{
		String res = new String();
		
		for(Chainon<E> curseur = tete; curseur != null; curseur = curseur.getSuivant())
		{
			res += curseur.getValeur() + " ";
		}
		
		return res;
	}

	/**
	 * getter and setter
	 */
	public Chainon<E> getQueue(){return queue;}
	public Chainon<E> getTete(){return tete;}
	public void setTete(Chainon<E> tete){this.tete = tete;}
	public void setQueue(Chainon<E> queue){this.queue = queue;}
	public Chainon<E> getCourant(){return courant;}
	public void setCourant(Chainon<E> courant){this.courant = courant;}	
	public void tailleMoinsUn(){this.taille = this.taille - 1;}
	public void setIndiceCourant(int i){this.indiceCourant = i;}
}
